.com
Hosted by:
Unit testing expertise at your fingertips!
Home | Discuss | Lists

Test Helper

The book has now been published and the content of this chapter has likely changed substanstially.
Please see page 643 of xUnit Test Patterns for the latest information.
Where do we put our test code when it is in reusable Test Utility Methods?

We define a helper class to hold any Test Utility Methods we want to reuse in several tests.

Sketch Test Helper Class embedded from Test Helper Class.gif

As we write tests, we will invariably find ourselves needing to repeat the same logic in many many tests. Initially, we will just "clone & twiddle" as we write additional tests that need the same logic. We introduce Test Utility Methods (page X) to hold this logic but where do we put such reusable logic?

A Test Helper is one option for where we can put reusable test logic.

How It Works

We define a separate class to hold the reusable Test Utility Methods that we wish to make accessible to several Testcase Classes (page X). In each test that wishes to use this logic, we access the logic using either class method calls or via an instance created specifically for the purpose.

When To Use It

We can use a Test Helper if we wish to share logic or variables between several Testcase Classes and cannot (or choose not to) find or define a Testcase Superclass (page X) that all the tests that require the logic could subclass from. This may be because our programming language doesn't support inheritance (e.g. Visual Basic 5 or 6), because we are already using inheritance for some other conflicting purpose or if the Test Utility Method needs access to specific types that are not visible from the Testcase Superclass.

The decision between Test Helper and Testcase Superclass all comes down to type visibility. The client classes need to be able to see the Test Utility Method and the Test Utility Method needs to be able to see all the types and classes it depends on. When it doesn't depend on many or when everything it depends on is visible from a single place, the Test Utility Method can be put into a common Testcase Superclass we define for our project or company. If it depends on types/classes that cannot be seen from a single place that all the clients can see, it may be necessary to put it on a Test Helper in the appropriate test package or subsystem. In larger systems with many groups of domain objects, it is common to have one Test Helper for each group (package) of related domain objects.

Variation: Test Fixture Registry

A Registry[PEAA] is a well-known object that can be accessed from anywhere in a program. There may be one or more instances and we don't really care. What is important is that we can use them to store and retrieve objects from different parts of our program or tests. (They are often confused with Singletons[GOF] which are also well known but have only a single instance.) A Test Fixture Registry gives the tests the ability to access the same fixture as other tests in the same test run. Depending on how we implement our Test Helper, we may choose to provide a different instance of the Test Fixture Registry for each Test Runner (page X) to prevent Test Run Wars (see Erratic Test on page X). A common example of a Test Fixture Registry is the Database Sandbox (page X).

A Test Fixture Registry is most commonly used with a Setup Decorator (page X). or with Lazy Setup (page X); it isn't needed with SuiteFixture Setup (page X) because only tests on the same Testcase Class need to share the fixture and using a fixture holding class variable works well for this.

Variation: Object Mother

The Object Mother pattern is simply an aggregate of a number of other patterns that each make a small but significant contribution to making the test fixture easier to manage. The Object Mother is one or more Test Helpers that provide Creation Methods (page X) and Attachment Methods (see Creation Method) that tests use to create ready-to-use test fixture objects. Object Mothers often provide several Creation Methods that create instances of the same class where each one results in a test object in a different starting state (a Named State Reaching Method (see Creation Method).) The Object Mother may also include the ability to delete the objects it creates. This is an example of Automated Teardown (page X).

Because there is no single, crisp definition of what someone means when they say "Object Mother", it is advisable to refer to the individual patterns (such as Automated Teardown) when referring to specific capabilities of the Object Mother.

Implementation Notes

The methods on the Test Helper can be implemented as either class methods or as instance methods depending on the degree to which we want to keep the tests from interacting.

Variation: Test Helper

If all the Test Utility Methods are stateless, the simplest approach is to implement all the functionality of the Test Helper as class methods and have the tests access them using the ClassName.methodName (or equivalent) notation. If we need to hold references to fixture objects, we could hold them in class variables but we need to be careful to avoid inadvertently creating a Shared Fixture (page X) (unless, of course, that is exactly what we are trying to do. In that case, we really building a Test Fixture Registry.)

Variation: Test Helper Object

If it isn't possible to use class methods for some reason, we can use instance methods. In this case, the client test will need to instantiate the Test Helper and store it in an instance variable; the methods can then be accessed via this variable. This is a good approach when the Test Helper is holding references to fixture or system under test (SUT) objects and we want to make sure that we don't creep into a Shared Fixture situation. It is also useful when the helper is being used to store expectations for a set of Mock Objects (page X) so that we can verify that the calls are interleaved between the Mock Objects correctly.

Motivating Example

The following is a Test Utility Method that is on the Testcase Class.

   public void testAddOneLineItem_quantity1() {
      Invoice inv = createAnonInvoice();
      LineItem expItem = new LineItem(inv, product, QUANTITY);
      // Exercise
      inv.addItemQuantity(product, QUANTITY);
      // Verify
      assertInvoiceContainsOnlyThisLineItem(inv, expItem);
   }
Example TestUtilityMethodUsage embedded from java/com/clrstream/camug/example/test/InvoiceTest.java
   void assertInvoiceContainsOnlyThisLineItem( Invoice inv,
                                     LineItem expItem) {
      List lineItems = inv.getLineItems();
      assertEquals("number of items", lineItems.size(), 1);
      LineItem actual = (LineItem)lineItems.get(0);
      assertLineItemsEqual("",expItem, actual);
   }
Example TestUtilityMethod embedded from java/com/clrstream/camug/example/test/InvoiceTest.java

This Test Utility Method is not reusable outside this particular class.

Refactoring Notes

We can make a Test Utility Method more reusable by moving it to a Test Helper class. This is often as simple as doing a Move Method[Fowler] refactoring to our Test Helper class. What can get in the way is if we have used instance variables to pass arguments to or return data from the Test Utility Method. This "global data" need to be converted to explicit arguments and return values before we can do the Move Method refactoring.

Example: Test Helper with Class Methods

In this modifed version of the test, we have made the Test Utility Method a class method on a Test Helper so we can access it via the classname without creating an instance:

   public void testAddOneLineItem_quantity1_staticHelper() {
      Invoice inv = createAnonInvoice();
      LineItem expItem = new LineItem(inv, product, QUANTITY);
      // Exercise
      inv.addItemQuantity(product, QUANTITY);
      // Verify
      TestHelper.assertContainsExactlyOneLineItem(inv, expItem);
   }
Example TestHelperStaticUsage embedded from java/com/clrstream/camug/example/test/InvoiceTest.java

Example: Test Helper with Instance Methods

Here, we have moved the Test Utility Method to a Test Helper as an instance method. Note that we must now access the method via an object reference (a variable that holds an instance of the Test Helper.)

   public void testAddOneLineItem_quantity1_instanceHelper() {
      Invoice inv = createAnonInvoice();
      LineItem expItem = new LineItem(inv, product, QUANTITY);
      // Exercise
      inv.addItemQuantity(product, QUANTITY);
      // Verify
      TestHelper helper = new TestHelper();
      helper.assertInvContainsExactlyOneLineItem(inv, expItem);
   }
Example TestHelperInstanceUsage embedded from java/com/clrstream/camug/example/test/InvoiceTest.java


Page generated at Wed Feb 09 16:39:44 +1100 2011

Copyright © 2003-2008 Gerard Meszaros all rights reserved

All Categories
Introductory Narratives
Web Site Instructions
Code Refactorings
Database Patterns
DfT Patterns
External Patterns
Fixture Setup Patterns
Fixture Teardown Patterns
Front Matter
Glossary
Misc
References
Result Verification Patterns
Sidebars
Terminology
Test Double Patterns
Test Organization
Test Refactorings
Test Smells
Test Strategy
Tools
Value Patterns
XUnit Basics
xUnit Members
All "Test Organization"
Named Test Suite
Test Code Reuse:
--Test Utility Method
--Parameterized Test
Testcase Class Structure:
--Testcase Class per Feature
--Testcase Class per Fixture
--Testcase Class per Class
Utility Method Location:
--Test Helper
----Test Fixture Registry
----Object Mother
----Test Helper
----Test Helper Object
--Testcase Superclass